package net.zio.kamenriderzioaddon.network;

import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.network.*;
import net.minecraftforge.network.simple.SimpleChannel;

import net.zio.kamenriderzioaddon.KamenRiderZioAddonMod;
import net.zio.kamenriderzioaddon.network.message.AddonPlayerVariablesSyncMessage;
import net.zio.kamenriderzioaddon.network.message.AddonSavedDataSyncMessage;
import net.zio.kamenriderzioaddon.network.message.SyncPlayerDataPacket;
import net.zio.kamenriderzioaddon.network.message.AnimationMessage;

import java.util.Optional;

/**
 * 网络处理类
 *
 * @author QianYunYi
 *
 * 处理客户端和服务器端的网络通信
 */
@Mod.EventBusSubscriber(modid = KamenRiderZioAddonMod.MODID, bus = Mod.EventBusSubscriber.Bus.MOD)
public final class NetworkHandler {
    // 网络协议版本
    private static final String PROTOCOL = "1";
    // 网络通道ID
    private static final ResourceLocation CHANNEL_ID =
            new ResourceLocation(KamenRiderZioAddonMod.MODID, "main");
    // 网络通道
    public static final SimpleChannel CHANNEL = NetworkRegistry.newSimpleChannel(
            CHANNEL_ID, () -> PROTOCOL, PROTOCOL::equals, PROTOCOL::equals);
    // 消息ID
    private static int id = 0;

    /**
     * 注册网络消息
     *
     * @param e 公共设置事件
     */
    @SubscribeEvent
    public static void onCommonSetup(FMLCommonSetupEvent e) {
        e.enqueueWork(() -> {

            // 注册玩家数据同步消息
            CHANNEL.registerMessage(
                    id++,
                    SyncPlayerDataPacket.class,
                    SyncPlayerDataPacket::encode,
                    SyncPlayerDataPacket::new,
                    (pkt, ctx) -> {
                        pkt.handle(ctx.get());
                        ctx.get().setPacketHandled(true);
                    },
                    Optional.of(NetworkDirection.PLAY_TO_CLIENT)
            );

            // 注册玩家变量同步消息
            CHANNEL.registerMessage(
                    id++,
                    AddonSavedDataSyncMessage.class,
                    AddonSavedDataSyncMessage::encode,
                    AddonSavedDataSyncMessage::new,
                    (pkt, ctx) -> {
                        pkt.handle(ctx.get());
                        ctx.get().setPacketHandled(true);
                    },
                    Optional.of(NetworkDirection.PLAY_TO_CLIENT)
            );

            // 注册玩家变量同步消息
            CHANNEL.registerMessage(
                    id++,
                    AddonPlayerVariablesSyncMessage.class,
                    AddonPlayerVariablesSyncMessage::encode,
                    AddonPlayerVariablesSyncMessage::new,
                    (pkt, ctx) -> {
                        pkt.handle(ctx.get());
                        ctx.get().setPacketHandled(true);
                    },
                    Optional.of(NetworkDirection.PLAY_TO_CLIENT)
            );

            // 注册动画消息
            CHANNEL.registerMessage(
                    id++,
                    AnimationMessage.class,
                    AnimationMessage::encode,
                    AnimationMessage::new,
                    (pkt, ctx) -> {
                        pkt.handle(ctx.get());
                        ctx.get().setPacketHandled(true);
                    },
                    Optional.of(NetworkDirection.PLAY_TO_CLIENT)
            );
        });
    }



    /* 工具方法 */

    /**
     * 向服务器发送消息
     *
     * @param msg 消息
     */
    public static void sendToServer(IMessage msg) {
        CHANNEL.sendToServer(msg);
    }

    /**
     * 向客户端发送消息
     *
     * @param player 玩家
     * @param msg 消息
     */
    public static void sendTo(ServerPlayer player, IMessage msg) {
        CHANNEL.sendTo(msg, player.connection.connection, NetworkDirection.PLAY_TO_CLIENT);
    }

    /**
     * 向所有玩家发送消息
     *
     * @param msg 消息
     */
    public static void sendToAll(IMessage msg) {
        CHANNEL.send(PacketDistributor.ALL.noArg(), msg);
    }

    /**
     * 向指定维度发送消息
     *
     * @param dim 维度
     * @param msg 消息
     */
    public static void sendToDimension(ResourceKey<Level> dim, IMessage msg) {
        CHANNEL.send(PacketDistributor.DIMENSION.with(() -> dim), msg);
    }

    /**
     * 向实体追踪玩家发送消息
     *
     * @param entity 实体
     * @param msg 消息
     */
    public static void sendToTracking(Entity entity, IMessage msg) {
        CHANNEL.send(PacketDistributor.TRACKING_ENTITY.with(() -> entity), msg);
    }

    /**
     * 向实体追踪玩家发送消息
     *
     * @param msg 消息
     * @param player 玩家
     */
    public static void sendToTrackingPlayers(IMessage msg, ServerPlayer player) {
        CHANNEL.send(PacketDistributor.TRACKING_ENTITY_AND_SELF.with(() -> player), msg);
    }

    /**
     * 向玩家发送消息
     *
     * @param msg 消息
     * @param player 玩家
     */
    public static void sendToPlayer(IMessage msg, ServerPlayer player) {
        CHANNEL.sendTo(msg, player.connection.connection, NetworkDirection.PLAY_TO_CLIENT);
    }
}